#include "Updater.h"

#include "Time.hpp"
#include "../World.hpp"
#include "../light/Light.hpp"
#include "../camera/Camera.hpp"
#include "../object/GameObject.hpp"
#include "../behaviour/Behaviour.hpp"
#include "../../game/object/Asteroid.h"
#include "../../game/object/Explosion.h"

void Updater::visit(World* world) {
    world->getTree()->clear();
    std::vector<GameObject*>* all = world->getChildren();

    for(std::vector<GameObject*>::iterator it = all->begin(); it != all->end(); it++) {
        world->getTree()->insert(*it);
    }

    if(Global::getCamera()) {
        Global::getCamera()->accept(this);
    }

    world->getTree()->detectCollisions();

    for(unsigned int i = 0; i < world->addLater.size(); ++i) {
        world->add(world->addLater.at(i));
    }

    world->addLater.clear();

    for(unsigned int i = 0; i < world->deleteLater.size(); ++i) {
        std::vector<GameObject*>::iterator it = std::find(world->getChildren()->begin(), world->getChildren()->end(), world->deleteLater.at(i));

        if(it != world->getChildren()->end()) {
            if(dynamic_cast<Asteroid*>(*it) != NULL && world->selectedAsteroid == *it) {
                world->selectedAsteroid = NULL;
            }

            delete(*it);
            world->getChildren()->erase(it);
        }
    }

    world->deleteLater.clear();

    visit((GameObject*) world);
}

void Updater::visit(Light* light) {
    visit((GameObject*) light);
}

void Updater::visit(Camera* camera) {
    visit((GameObject*) camera);
}

void Updater::visit(GameObject* object) {
    if(object->getBehaviour()) {
        object->getBehaviour()->update(Time::getInstance()->step());
    }

    for(std::vector< GameObject* >::iterator i = object->getChildren()->begin(); i != object->getChildren()->end(); ++i) {
        (*i)->accept(this);
    }

    Global::getWorld()->getTree()->insert(object);
}

void Updater::visit(Collider* collider) {

}

void Updater::visit(Asteroid* asteroid) {
    asteroid->addLocation(asteroid->getVelocity() * Time::getInstance()->step());

    glm::vec3 velocity = asteroid->getVelocity();
    glm::vec3 location = asteroid->getLocation();

    if(location.x < -Global::getWorld()->LEVEL_SIZE || location.x > Global::getWorld()->LEVEL_SIZE) {
        velocity.x = -velocity.x;
    }

    if(location.y < -Global::getWorld()->LEVEL_SIZE || location.y > Global::getWorld()->LEVEL_SIZE) {
        velocity.y = -velocity.y;
    }

    if(location.z < -Global::getWorld()->LEVEL_SIZE || location.z > Global::getWorld()->LEVEL_SIZE) {
        velocity.z = -velocity.z;
    }

    asteroid->setVelocity(velocity);

    visit((GameObject*) asteroid);
}

void Updater::visit(Rocket* rocket) {
    rocket->addLocation(rocket->getVelocity() * Time::getInstance()->step());

    if(rocket->getLocation().x < -Global::getWorld()->LEVEL_SIZE || rocket->getLocation().x > Global::getWorld()->LEVEL_SIZE || rocket->getLocation().y < -Global::getWorld()->LEVEL_SIZE || rocket->getLocation().y > Global::getWorld()->LEVEL_SIZE || rocket->getLocation().z < -Global::getWorld()->LEVEL_SIZE || rocket->getLocation().z > Global::getWorld()->LEVEL_SIZE) {
        Global::getWorld()->deleteLater.push_back(rocket);
        return;
    }

    visit((GameObject*) rocket);
}

void Updater::visit(Explosion* explosion) {
    visit((GameObject*) explosion);
    explosion->addTime(Time::getInstance()->step());
}
